package puzzle_a_estrela;

public class NPuzzle {

    private static Heuristica heuristica = new Heuristica();

    public String executar() {
        String output = " ";
        int tamTabuleiro = 4;
        int numEmbaralhamentos = 20;

        // Cria um estado inicial aleatorio e memoriza o estado
        NPuzzleEstado estado1 = randomPuzzle(tamTabuleiro, numEmbaralhamentos);
        NPuzzleEstado estado2 = new NPuzzleEstado(estado1);

        // Resolve o problema usando busca a*
        Acao[] acoesBuscaGulosa = resolverBuscaGulosa(new NPuzzleEstado(estado1));
        Acao[] acoesAEstrela = resolverAEstrela(new NPuzzleEstado(estado1));

        // Lista o estado inicial e os resultados das acões realizadas
        output += ">> NÓ INICIAL: \n";
        output += estado2.toString() + "\n";
        output += ">> SOLUÇÃO VIA BUSCA GULOSA: \n";
        for (int i = 0; i < acoesBuscaGulosa.length; i++) {
            output += "Iteração " + (i + 1) + ": " + acoesBuscaGulosa[acoesBuscaGulosa.length - 1 - i] + " \n";
            NPuzzleEstado.fazAcao(estado2, acoesBuscaGulosa[acoesBuscaGulosa.length - 1 - i]);
            output += estado2.toString() + "\n ";
        }
        
        output += "=================================================================================================\n";
        // Lista o estado inicial e os resultados das acões realizadas
        output += ">> Nó inicial: \n";
        output += estado2.toString() + "\n";

        output += ">> SOLUÇÃO VIA A*: \n";
        estado2 = new NPuzzleEstado(estado1);
        for (int i = 0; i < acoesAEstrela.length; i++) {
            output += "Iteração " + (i + 1) + ": " + acoesAEstrela[acoesAEstrela.length - 1 - i] + " \n";
            NPuzzleEstado.fazAcao(estado2, acoesAEstrela[acoesAEstrela.length - 1 - i]);
            output += estado2.toString() + "\n ";
        }
        output += ">> Fim da Execução \n";

        return output;
    }

    public static Acao[] resolverBuscaGulosa(NPuzzleEstado estado) {
        // resolver o problema usando a busca a*
        No objetivo = No.buscaGulosa(estado, heuristica);
        Acao[] actions;

        //Verifica se o objetivo foi alcançado e retorna a ações.
        if (objetivo != null) {
            //Determina todas as ações que foram feitas para se chegar nesse estado
            actions = objetivo.getAcoes();
        } else {
            actions = new Acao[0];
        }

        return actions;
    }

    public static Acao[] resolverAEstrela(NPuzzleEstado estado) {
        // resolver o problema usando a busca a*
        No objetivo = No.buscaAEstrela(estado, heuristica);
        Acao[] actions;

        //Verifica se o objetivo foi alcançado e retorna a ações.
        if (objetivo != null) {
            //Determina todas as ações que foram feitas para se chegar nesse estado
            actions = objetivo.getAcoes();
        } else {
            actions = new Acao[0];
        }

        return actions;
    }

    /**
     * Gera uma configuração aleatória do tabuleiro
     */
    public static NPuzzleEstado randomPuzzle(int tamTabuleiro, int nEmbalhado) {
        NPuzzleEstado myState = new NPuzzleEstado(tamTabuleiro);
        int totalMoves = 0;
        while (totalMoves < nEmbalhado) {
            double r = Math.random();
            try {
                if (r < 0.25) {
                    NPuzzleEstado.fazAcao(myState, NPuzzleEstado.MOVE_ESQUERDA);
                } else if (r < 0.5) {
                    NPuzzleEstado.fazAcao(myState, NPuzzleEstado.MOVE_DIREITA);
                } else if (r < 0.75) {
                    NPuzzleEstado.fazAcao(myState, NPuzzleEstado.MOVE_CIMA);
                } else {
                    NPuzzleEstado.fazAcao(myState, NPuzzleEstado.MOVE_BAIXO);
                }
                totalMoves++;
            } catch (RuntimeException e) {
                ; // Movimento Ilegal
            }
        }
        return myState;
    }

    /**
     * Verifica se as ações resolvem o problema
     */
    public static boolean verificaAcoes(NPuzzleEstado estado, Acao[] acoes) {
        for (int i = 0; i < acoes.length; i++) {
            try {
                NPuzzleEstado.fazAcao(estado, acoes[i]);
            } catch (RuntimeException e) {
                return false; // Movimento ilegal
            }
        }

        // Verifica se o estado é final
        return estado.estadoFinal();
    }
}
